package com.clp.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.net.URI;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class HttpUtil {
    /**
     * GET请求，查询参数格式
     *
     * @param url                   请求地址
     * @param headers               请求头，可以没有
     * @param params                查询参数
     * @param isRetry               是否重试
     * @param connectTimeoutSeconds 建立连接的最大等待时间
     * @param readTimeoutSeconds    响应的最大等待时间
     * @return 结果为字符串，调用方自己反序列化
     */
    public static String sendGet(String url,
                                 Map<String, String> headers,
                                 Map<String, String> params,
                                 boolean isRetry,
                                 int connectTimeoutSeconds,
                                 int readTimeoutSeconds) {
        String result = doSendGet(url, headers, params, connectTimeoutSeconds, readTimeoutSeconds);
        if (StringUtil.isBlank(result) && isRetry) {
            log.error("sengGet retry url: {}", url);
            doSendGet(url, headers, params, connectTimeoutSeconds, readTimeoutSeconds);
        }
        return result;
    }

    private static String doSendGet(String url,
                                    Map<String, String> headers,
                                    Map<String, String> params,
                                    int connectTimeout,
                                    int readTimeout) {
        String result = "";

        try {
            if (StringUtil.isBlank(url)) {
                return result;
            }

            RestTemplate restTemplate = assembledSimpleRestTemplate(connectTimeout, readTimeout);

            // 设置headers
            HttpHeaders httpHeaders = new HttpHeaders();
            if (MapUtils.isNotEmpty(headers)) {
                headers.forEach(httpHeaders::add);
            }

            // 设置查询参数
            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url);
            URI uri = null;
            if (MapUtils.isNotEmpty(params)) {
                MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
                params.forEach(multiValueMap::add);
                uri = uriComponentsBuilder.queryParams(multiValueMap).build().toUri();
            } else {
                uri = uriComponentsBuilder.build().toUri();
            }


            HttpEntity<String> requestEntity = new HttpEntity(null, httpHeaders);
            ResponseEntity<String> responseEntity = restTemplate.exchange(Objects.requireNonNull(uri), HttpMethod.GET, requestEntity, String.class);
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                log.error("sendGet status error, url: {}, status: {}", url, responseEntity.getStatusCode());
                return result;
            }
            result = responseEntity.getBody();

        } catch (Exception e) {
            log.error("sendGet request error, url: {}", url, e);
        }

        return result;
    }

    /**
     * POST请求，JSON格式请求
     *
     * @param url
     * @param headers
     * @param bodyJson
     * @param isRetry
     * @param connectTimeoutSeconds
     * @param readTimeoutSeconds
     * @return
     */
    public static String sendPostJson(String url,
                                      Map<String, String> headers,
                                      String bodyJson,
                                      boolean isRetry,
                                      int connectTimeoutSeconds,
                                      int readTimeoutSeconds) {

        String result = doSendPostJson(url, headers, bodyJson, connectTimeoutSeconds, readTimeoutSeconds);
        if (isRetry && StringUtil.isBlank(result)) {
            log.error("sendPostJson retry, url: {}", url);
            result = doSendPostJson(url, headers, bodyJson, connectTimeoutSeconds, readTimeoutSeconds);
        }

        return result;
    }

    private static String doSendPostJson(String url,
                                         Map<String, String> headers,
                                         String bodyJson,
                                         int connectTimeoutSeconds,
                                         int readTimeoutSeconds) {

        String result = "";

        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            if (MapUtils.isNotEmpty(headers)) {
                headers.forEach(httpHeaders::add);
            }
            HttpEntity<String> httpEntity = new HttpEntity<>(bodyJson, httpHeaders);
            RestTemplate restTemplate = assembledSimpleRestTemplate(connectTimeoutSeconds, readTimeoutSeconds);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                log.error("doSendPost status error, url: {}, status: {}", url, responseEntity.getStatusCode());
                return result;
            }
            result = responseEntity.getBody();

        } catch (Exception e) {
            log.error("doSendPost error!", e);
        }

        return result;
    }

    /**
     * POST请求，表单格式参数
     *
     * @param url
     * @param headers
     * @param params
     * @param isRetry
     * @param connectTimeoutSeconds
     * @param readTimeoutSeconds
     * @return
     */
    public static String sendPostFormData(String url,
                                          Map<String, String> headers,
                                          Map<String, String> params,
                                          boolean isRetry,
                                          int connectTimeoutSeconds,
                                          int readTimeoutSeconds) {

        String result = doSendPostFormData(url, headers, params, connectTimeoutSeconds, readTimeoutSeconds);
        if (isRetry && StringUtil.isBlank(result)) {
            log.error("sendPostFormData retry, url: {}", url);
            result = doSendPostFormData(url, headers, params, connectTimeoutSeconds, readTimeoutSeconds);
        }

        return result;
    }

    private static String doSendPostFormData(String url,
                                             Map<String, String> headers,
                                             Map<String, String> params,
                                             int connectTimeoutSeconds,
                                             int readTimeoutSeconds) {

        String result = "";

        try {
            HttpHeaders httpHeaders = new HttpHeaders();
            httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            if (MapUtils.isNotEmpty(headers)) {
                headers.forEach(httpHeaders::add);
            }

            MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap();
            if (MapUtils.isNotEmpty(params)) {
                params.forEach(multiValueMap::add);
            }

            HttpEntity<String> httpEntity = new HttpEntity(multiValueMap, httpHeaders);
            RestTemplate restTemplate = assembledSimpleRestTemplate(connectTimeoutSeconds, readTimeoutSeconds);
            ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, httpEntity, String.class);
            if (responseEntity.getStatusCode() != HttpStatus.OK) {
                log.error("doSendPostFormData status error, url: {}, status: {}", url, responseEntity.getStatusCode());
                return result;
            }
            result = responseEntity.getBody();

        } catch (Exception e) {
            log.error("doSendPostFormData error!", e);
        }

        return result;
    }

    private static RestTemplate assembledSimpleRestTemplate(int connectTimeoutSeconds, int readTimeoutSeconds) {
        RestTemplate restTemplate = new RestTemplate();
        SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
        if (connectTimeoutSeconds > 0) {
            simpleClientHttpRequestFactory.setConnectTimeout(connectTimeoutSeconds * 1000);
        }
        if (readTimeoutSeconds > 0) {
            simpleClientHttpRequestFactory.setReadTimeout(readTimeoutSeconds * 1000);
        }
        restTemplate.setRequestFactory(simpleClientHttpRequestFactory);
        return restTemplate;
    }
}
